import io
import os.path
import uuid
from datetime import timedelta
from urllib.parse import quote

import jwt
import xlwt
from django.core.cache import cache
from django.db import DatabaseError
from django.db.models import Q
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import redirect
from django.utils import timezone
from rest_framework.decorators import api_view, throttle_classes
from rest_framework.response import Response

from polls.captcha import Captcha
from polls.models import Teacher, User
from polls.utils import random_captcha_code, to_md5_hex, send_message_by_sms, random_mobile_code, check_username, \
    check_password, check_tel, update_last_visit, upload_stream_to_qiniu, POOL
from vote.settings import SECRET_KEY


def show_index(request):
    """显示首页"""
    return redirect('/static/html/subjects.html')


def get_captcha(request: HttpRequest) -> HttpResponse:
    """生成验证码图片"""
    code = random_captcha_code()
    request.session['captcha'] = code
    image_data = Captcha.instance().generate(code)
    return HttpResponse(image_data, content_type='image/png')


@api_view(('POST', ))
def login(request: HttpRequest) -> HttpResponse:
    """登录"""
    username = request.data.get('username')
    password = request.data.get('password')
    if check_username(username) and check_password(password):
        password = to_md5_hex(password)
        user = User.objects\
            .filter(Q(username=username) | Q(tel=username))\
            .filter(password=password).first()
        if user:
            update_last_visit.apply_async((user, ), serializer='pickle')
            # 用户登录成功，向用户签发身份令牌（JWT）
            payload = {
                'userid': user.no,
                'exp': timezone.now() + timedelta(days=1)
            }
            # 通过三方库PyJWT生成Json Web Token作为给用户签发的身份令牌
            # 这个令牌要通过返回的JSON格式的数据返回到用户浏览器中，用户浏览器保存token
            # 后续的请求可以在请求头中带上token，服务器通过token确认用户身份
            token = jwt.encode(payload, SECRET_KEY).decode()
            data = {
                'code': 20000, 'message': '登录成功',
                'token': token, 'username': user.username
            }
        else:
            data = {'code': 20001, 'message': '用户名或密码错误'}
    else:
        data = {'code': 20002, 'message': '无效的用户名或密码'}
    return Response(data)


@api_view(('POST', ))
def register(request: HttpRequest) -> HttpResponse:
    """注册"""
    agreement = request.data.get('agreement')
    if agreement == 'true':
        # code_from_user = request.POST.get('mobilecode', '0')
        # code_from_sess = request.session.get('mobilecode', '1')
        # # 让使用过的手机验证码失效
        # request.session['mobilecode'] = ''
        # if code_from_user == code_from_sess:
        username = request.data.get('username')
        password = request.data.get('password')
        tel = request.data.get('tel')
        photo = request.data.get('photo')
        if photo.size <= 2621440:
            if check_username(username) and check_password(password) \
                    and check_tel(tel):
                user = User()
                user.username = username
                user.password = to_md5_hex(password)
                user.tel = tel
                if photo:
                    ext = os.path.splitext(photo.name)[1]
                    filename = f'{uuid.uuid1().hex}{ext}'
                    # 调用线程池的submit方法可以指定把什么样的任务放到线程中执行
                    # 调用submit方法会返回Future对象，可以在将来的某个时候获取线程的执行结果
                    future = POOL.submit(upload_stream_to_qiniu, photo, filename, photo.size)
                    # Future对象的add_done_callback方法可以绑定线程执行完成后要执行的回调函数（钩子函数）
                    # 这样的话在线程执行结束时，可以指定需要自动执行的代码
                    # future.add_done_callback(...)
                    # upload_stream_to_qiniu(photo, filename, photo.size)
                    user.photo = f'http://qfcgif559.hn-bkt.clouddn.com/{filename}'
                try:
                    user.save()
                    data = {'code': 30000, 'message': '注册成功，请登录'}
                except DatabaseError:
                    data = {'code': 30001, 'message': '用户名或手机号已被注册'}
            else:
                data = {'code': 30002, 'message': '请输入有效的注册信息'}
        else:
            data = {'code': 30004, 'message': '用户头像文件大小超过2.5M'}
        # else:
        #     hint = '请输入正确的手机验证码'
    else:
        data = {'code': 30003,  'message': '请勾选同意网站用户协议及隐私政策'}
    return Response(data)


def logout(request: HttpRequest) -> HttpResponse:
    """注销"""
    request.session.flush()
    return redirect('/login/')


def send_mobile_code(request: HttpRequest, tel) -> HttpResponse:
    """发送短信验证码"""
    if check_tel(tel):
        if cache.get(f'vote:polls:block:{tel}'):
            data = {'code': 20002, 'message': '请不要在120秒内重复发送短信验证码'}
        else:
            code = random_mobile_code()
            request.session['mobilecode'] = code
            message = f'您的短信验证码是{code}，打死也不能告诉别人哟。【Python小课】'
            # 异步化执行发送短信操作（消息的生产者：把任务放到消息队列）
            send_message_by_sms.delay(tel=tel, message=message)
            cache.set(f'vote:polls:block:{tel}', code, timeout=120)
            data = {'code': 20000, 'message': '短信验证码已经发送到您的手机'}
    else:
        data = {'code': 20001, 'message': '请输入正确的手机号'}
    return JsonResponse(data)


def is_unique_username(request: HttpRequest) -> HttpResponse:
    """检查用户名唯一性"""
    username = request.GET.get('username')
    if check_username(username):
        if User.objects.filter(username=username).exists():
            data = {'code': 30001, 'message': '用户名已被注册'}
        else:
            data = {'code': 30000, 'message': '用户名可以使用'}
    else:
        data = {'code': 30002, 'message': '无效的用户名'}
    return JsonResponse(data)


def export_excel(request: HttpRequest) -> HttpResponse:
    """导出Excel报表文件"""
    wb = xlwt.Workbook()
    sheet = wb.add_sheet('投票数据统计表')
    titles = ('姓名', '所属学科', '好评数', '差评数')
    for col, title in enumerate(titles):
        sheet.write(0, col, title)
    queryset = Teacher.objects.all()\
        .only('name', 'subject__name', 'good_count', 'bad_count')\
        .select_related('subject').order_by('-good_count')
    props = ('name', 'subject', 'good_count', 'bad_count')
    for row, teacher in enumerate(queryset):
        for col, prop in enumerate(props):
            # 通过getattr函数动态获取指定对象的属性（反射）
            value = str(getattr(teacher, prop, ''))
            sheet.write(row + 1, col, value)
    # 将工作簿的内容写入BytesIO中
    buffer = io.BytesIO()
    wb.save(buffer)
    # 通过BytesIO对象的getvalue方法获得bytes对象（字节串）写入HTTP响应
    resp = HttpResponse(buffer.getvalue())
    # 添加HTTP响应头（以键值对的方式写入）
    resp['Content-Type'] = 'application/vnd.ms-excel'
    # 使用urllib.parse模块的quote函数将中文处理成百分号编码
    filename = quote('投票数据统计.xls')
    # inline - 表示浏览器直接内联打开文件；attachment - 表示浏览器以附件的方式下载文件
    resp['Content-Disposition'] = f'attachment; filename*=utf-8\'\'{filename}'
    return resp
